package dao

import (
	"errors"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"homepage_srv/homepage"
	"homepage_srv/model"
	"smart-health-platforms/config"
	"smart-health-platforms/pkg"

	"strconv"
	"strings"
	"time"
)

// 添加购物车
func CartCreate(userId, goodId, skuId int, num int) error {
	out, err := CheckGoodsIsSoldOut(goodId) //查看商品是否下架
	if err != nil {
		pkg.Error("获取商品状态失败", zap.Error(err))
		return err
	}
	if out == true {
		pkg.Error("商品已下架请选择其他商品")
		return nil
	}
	bo := GetCartGoods(userId, goodId, skuId) //查看购物车是否有该商品
	if bo == true {
		err := OrderGoodsStock(userId, goodId, skuId, num) //如果购物车中存在该商品，修改库存
		if err != nil {
			pkg.Error("更新库存失败", zap.Error(err))
			return err
		}
		return nil
	}
	var good model.Good
	good.Id = uint64(goodId)
	err = good.Detail()
	if err != nil {
		pkg.Error("查询失败", zap.Error(err))
		return err
	}
	var sku model.GoodSku
	sku.Id = uint32(skuId)
	err = sku.GetGoodSuk()
	if err != nil {
		return err
	}
	if int(sku.Stock) < num {
		pkg.Warn("商品库存不足")
		return errors.New("商品库存不足")
	}
	goodMap := map[string]interface{}{
		"name":    good.StoreName,
		"num":     num,
		"userId":  userId,
		"goodsId": goodId,
		"skuId":   skuId,
		"price":   sku.Price,
		"image":   sku.Image,
		"AddTime": time.Now(),
		"expired": false,
	}
	expire := 24 * time.Hour
	err = CartAdd(userId, goodId, goodMap, skuId, expire)
	if err != nil {
		pkg.Error("添加购物车失败", zap.Error(err))
		return err
	}
	return nil
}

// 添加商品到购物车
func CartAdd(userId, goodId int, goodMap map[string]interface{}, sukId int, expire time.Duration) error {
	key := fmt.Sprintf("cart:%d:%d:%d", userId, goodId, sukId)
	err := config.Rdb.HMSet(config.Ctx, key, goodMap).Err()
	if err != nil {
		pkg.Error("添加购物车失败")
		return err
	}
	if expire <= 0 {
		pkg.Warn("添加购物车失败")
		return errors.New("过期时间必须大于0")
	}
	if err = config.Rdb.Expire(config.Ctx, key, expire).Err(); err != nil {
		pkg.Error("设置过期时间失败")
		return fmt.Errorf("设置过期时间失败: %w", err)
	}
	return nil
}

// 检查购物车中是否有该商品
func GetCartGoods(userId, goodId int, skuId int) bool {
	key := fmt.Sprintf("cart:%d:%d:%d", userId, goodId, skuId)
	return config.Rdb.Exists(config.Ctx, key).Val() == 1
}

// 更新购物车商品库存
func OrderGoodsStock(userId, goodId int, skuId, num int) error {
	key := fmt.Sprintf("cart:%d:%d:%d", userId, goodId, skuId)
	return config.Rdb.HIncrBy(config.Ctx, key, "num", int64(num)).Err()
}
func CheckGoodsIsSoldOut(goodsId int) (bool, error) {
	var good model.Good
	good.Id = uint64(uint(goodsId))
	err := good.Detail()
	if err != nil {
		// 商品查询失败：区分"记录不存在"和"其他错误"
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// 商品不存在 → 视为"已下架"（业务逻辑：无此商品等同于下架）
			return true, nil
		}
		pkg.Error("商品查询失败", zap.Error(err), zap.Int("goods_id", goodsId))
		return false, err // 其他错误透传
	}
	// 商品存在 → 返回实际下架状态（假设模型有 IsSoldOut 字段）
	return false, nil // false=未下架，true=已下架
}

// 购物车列表
func CartList(userId int) (CartList []*homepage.CartList, err error) {
	keyPattern := fmt.Sprintf("cart:%d:*:*", userId)
	keys, err := config.Rdb.Keys(config.Ctx, keyPattern).Result()
	if err != nil {
		return nil, fmt.Errorf("获取购物车键列表失败: %w", err)
	}
	for _, cartKey := range keys {
		// 1. 判断商品是否过期
		ttl, err := config.Rdb.TTL(config.Ctx, cartKey).Result()
		if err != nil {
			pkg.Warn("获取商品过期时间失败", zap.Error(err), zap.String("cart_key", cartKey))
			continue
		}
		isExpired := ttl == -2
		// 2. 获取商品详情
		cart, err := config.Rdb.HGetAll(config.Ctx, cartKey).Result()
		if err != nil {
			pkg.Warn("获取商品详情失败", zap.Error(err), zap.String("cart_key", cartKey))
			continue
		}
		// 3. 解析商品字段
		goodsId, err := strconv.Atoi(cart["goodsId"])
		if err != nil {
			pkg.Warn("解析商品ID失败", zap.Error(err), zap.String("goodsId", cart["goodsId"]))
			continue
		}
		num, _ := strconv.Atoi(cart["num"])
		price, _ := strconv.ParseFloat(cart["price"], 64)
		// 4. 判断商品是否已下架
		isSoldOut, err := CheckGoodsIsSoldOut(goodsId)
		if err != nil {
			pkg.Warn("查询商品状态失败", zap.Error(err), zap.Int("goodsId", goodsId))
			// 可根据业务决定：是标记为下架还是继续视为正常
			isSoldOut = false // 默认为正常，避免误判
		}
		// 5. 构建商品对象（新增下架标识）
		goods := &homepage.CartList{
			Name:       cart["name"],
			Price:      float32(price),
			Num:        int64(num),
			UserId:     int64(userId),
			GoodsId:    int64(goodsId),
			TotalPrice: int64(num * int(price)),
			Expired:    isExpired, // 过期状态
			IsSoldOut:  isSoldOut, // 新增：下架状态
		}
		CartList = append(CartList, goods)
	}
	return CartList, nil
}

// 购物车列表搜索商品
func CartSearch(userId int, searchKey string) (CartList []*homepage.CartSearchList, err error) {
	keyPattern := fmt.Sprintf("cart:%d:*", userId)
	// 获取所有匹配的键（带错误处理）
	keys, err := config.Rdb.Keys(config.Ctx, keyPattern).Result()
	if err != nil {
		return nil, fmt.Errorf("获取购物车键列表失败: %w", err)
	}
	lowerSearchKey := strings.ToLower(searchKey)
	isSearch := searchKey != ""
	for _, cartKey := range keys {
		// 1. 判断商品是否过期（通过TTL命令）
		ttl, err := config.Rdb.TTL(config.Ctx, cartKey).Result()
		if err != nil {
			pkg.Warn("获取商品过期时间失败", zap.Error(err), zap.String("cart_key", cartKey))
			continue
		}
		isExpired := ttl == -2 // ttl=-2表示键已过期
		// 2. 获取商品详情
		cart, err := config.Rdb.HGetAll(config.Ctx, cartKey).Result()
		if err != nil {
			pkg.Warn("获取商品详情失败", zap.Error(err), zap.String("cart_key", cartKey))
			continue
		}
		// 3. 解析商品字段（修正拼写错误 goosId -> goodsId）
		goodsIdStr := cart["goodsId"]
		goodsId, err := strconv.Atoi(goodsIdStr)
		if err != nil {
			pkg.Warn("解析商品ID失败", zap.Error(err), zap.String("goodsId", goodsIdStr))
			continue
		}
		num, _ := strconv.Atoi(cart["num"])
		price, _ := strconv.ParseFloat(cart["price"], 64)
		goodsName := cart["name"]
		// 4. 搜索过滤（不区分大小写）
		if isSearch && !strings.Contains(strings.ToLower(goodsName), lowerSearchKey) {
			continue
		}
		// 5. 判断商品是否已下架（从商品库查询状态）
		isSoldOut, err := CheckGoodsIsSoldOut(goodsId) // 复用之前的商品状态查询函数
		if err != nil {
			pkg.Warn("查询商品下架状态失败", zap.Error(err), zap.Int("goodsId", goodsId))
			isSoldOut = false // 异常时默认视为未下架
		}
		// 6. 构建结果（包含过期和下架状态）
		CartList = append(CartList, &homepage.CartSearchList{
			Name:       goodsName,
			Price:      float32(price),
			Num:        int64(num),
			UserId:     int64(userId),
			GoodsId:    int64(goodsId),
			TotalPrice: int64(num * int(price)),
			Expired:    isExpired, // 新增：是否过期
			IsSoldOut:  isSoldOut, // 新增：是否下架
		})
	}
	return CartList, nil
}

// 清空购物车
func DelCart(userId int) {
	key := fmt.Sprintf("cart:%d:*", userId)
	keys := config.Rdb.Keys(config.Ctx, key).Val()
	for _, s := range keys {
		config.Rdb.Del(config.Ctx, s)
	}
}

// 删除购物车指定商品
func CartDelGoods(userId, goodId int) error {
	key := fmt.Sprintf("cart:%d:%d", userId, goodId)
	return config.Rdb.Del(config.Ctx, key).Err()
}

// 增加购物车中商品的数量
func CartIncreaseGoodsNum(userId, goodId int, skuId int) error {
	// 检查商品是否下架
	isSoldOut, err := CheckGoodsIsSoldOut(goodId)
	if err != nil {
		pkg.Error("获取商品状态失败", zap.Error(err))
		return err
	}
	if isSoldOut {
		return errors.New("商品已下架，无法修改数量")
	}
	// 增加数量
	key := fmt.Sprintf("cart:%d:%d:%d", userId, goodId, skuId)
	return config.Rdb.HIncrBy(config.Ctx, key, "num", 1).Err()
}

// 减少购物车中商品的数量
func CartDecreaseGoodsNum(userId, goodId int, skuId int) error {
	key := fmt.Sprintf("cart:%d:%d:%d", userId, goodId, skuId)

	// 获取当前数量
	currentNumStr, err := config.Rdb.HGet(config.Ctx, key, "num").Result()
	if err != nil {
		pkg.Error("获取商品当前数量失败", zap.Error(err))
		return err
	}

	currentNum, err := strconv.Atoi(currentNumStr)
	if err != nil {
		pkg.Error("解析商品数量失败", zap.Error(err))
		return err
	}
	// 计算减少后的数量
	newNum := currentNum - 1
	if newNum == 1 {
		// 如果数量小于等于0，直接从购物车中删除
		pkg.Warn("商品数量不能小于1，无法继续减少")
		return errors.New("商品数量不能小于1，无法继续减少")
	}
	// 更新数量
	return config.Rdb.HSet(config.Ctx, key, "num", newNum).Err()
}

// 批量删除购物车中的商品
func CartBatchDeleteGoods(userId int, goodIds []int64) error {
	if len(goodIds) == 0 {
		return errors.New("商品ID列表不能为空")
	}
	// 构建所有要删除的键
	keys := make([]string, 0, len(goodIds))
	for _, goodId := range goodIds {
		keys = append(keys, fmt.Sprintf("cart:%d:%d:*", userId, goodId))
	}
	// 批量删除
	result := config.Rdb.Del(config.Ctx, keys...)
	if result.Err() != nil {
		pkg.Error("批量删除购物车商品失败", zap.Error(result.Err()))
		return result.Err()
	}
	// 检查是否有部分删除失败（Redis的Del命令会返回成功删除的数量）
	if int(result.Val()) != len(goodIds) {
		pkg.Warn("部分商品删除失败", zap.Int("expected", len(goodIds)), zap.Int64("actual", result.Val()))
	}
	return nil
}
